﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Threading;
using System.Net;
using System.Net.Sockets;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Windows.Forms;

namespace OINS
{
    class Server2
    {
        bool koniec = false;
        List<Record> list;
        List<Client2> clients;
        String sciezka;
        bool suspend = false;

        /* Konststruktor pobierajacy referencje do listy plikow */
        public Server2(List<Record> list, ref List<Client2> clients)
        {
            this.list = list;
          
            this.clients = clients;
            
        }
        public void LoadTheDir()
        {
            try
            {
                FileInfo list = new FileInfo("dir.txt");
                StreamReader stream = list.OpenText();
                sciezka = stream.ReadLine();
                stream.Close();
            }
            catch (System.IO.IOException)
            {
                sciezka = "";
            }
        }
        /* Funkcja zwracająca IP */
        public String GetMyIP()
        {
            IPHostEntry ip = System.Net.Dns.GetHostByName(System.Net.Dns.GetHostName());
            return ip.AddressList[0].ToString();
        }
        public void Koniec()
        {
            koniec = true;
        }
        public void pokazKoniec()
        {
            System.Console.WriteLine(koniec);
        }
        public void KonczWatek()
        {
            suspend = true;
        }

        /* Zagnieżdzona klasa obsługująca klienta*/
        class ClientHandler
        {
            List<String> askers = new List<String>();
            List<String> downers = new List<String>();
            private byte[] buffer;
            private List<int> temp = new List<int>();
            private Socket socket;
            private Stream inputstream;
          
            private NetworkStream networkStream;
       
            private Server2 parent;
            private String directory;
            String asker;
            System.IO.StreamWriter streamwriter;
            System.IO.StreamReader streamreader;
            Thread a;
           
            String odbiorca;
            bool route = false;

            String request;

            /* Konstruktor pobierajacy gniazdo do którego podłączony jest klient, oraz referencje do Servera */
            public ClientHandler(Socket socketForClient, Server2 parent)
            {
                socket = socketForClient;
                buffer = new byte[256];
                networkStream = new NetworkStream(socketForClient);
   
                this.parent = parent;
                streamwriter = new System.IO.StreamWriter(networkStream);
                streamreader = new System.IO.StreamReader(networkStream);
            }

            /* Funkcja sprawdzająca istnienie pliku na serwerze */
            public bool CheckAvailibity(string filename, ref string directory)
            {
                foreach (Record file in parent.list)
                {
                    if (file.FileName == filename)
                    {
                        directory = file.Directory;
                        return true;
                    }
                }
                return false;

            }

           
            private void Send(String dir, String filename)
            {
                byte[] fileBuffer;
                Stream fileStream = File.OpenRead(dir+filename);
                long temp = fileStream.Length;
                int cos;
                do
                {
                    fileBuffer = new byte[256];
                    cos = fileStream.Read(fileBuffer, 0, 256);
                    if (temp < 256)
                    {
                        networkStream.Write(fileBuffer, 0, (int)temp);
                        temp = 0;
                    }
                    else
                    {
                        networkStream.Write(fileBuffer, 0, 256);
                        temp -= 256;
                    }
                    Console.WriteLine(temp);
                } while (temp > 0);
                System.Console.WriteLine("jestem tu");
                fileStream.Close();
            }
            public void Read(String filename)
            {
                parent.LoadTheDir();
                Stream fileStream = File.OpenWrite(parent.sciezka+"/"+filename);
                byte[] fileBuffer = new byte[256];
                networkStream.ReadTimeout = 2;
                int cos;
                do
                {
                    try
                    {
                        cos = networkStream.Read(fileBuffer, 0, 256);
                    }
                    catch (System.IO.IOException)
                    {
                        cos = 0;
                    }

                    System.Console.WriteLine("to jest cos {0}", cos);
                    fileStream.Write(fileBuffer, 0, cos);
                } while (cos > 0);
                System.Console.WriteLine("dziala ");
                fileStream.Close();
            }


            public void funkcja()
            {
                do
                {
                    String filenameSearch;
                    try
                    {
                        request = streamreader.ReadLine();
                    }
                    catch (System.IO.IOException)
                    {
                        request = "brak";
                    }

                    System.Console.WriteLine("SERW: request ---------" + request);
                    try
                    {
                        String[] temp = request.Split(' ');
                        request = temp[0];
                        if (temp[0] == "Search")
                        {
                            odbiorca = temp[1];
                        }
                        if (temp[0] == "NOTFOUND")
                        {
                            MessageBox.Show("NOT FOUND");
                        }

                        if (temp[0] == "SENDING")
                        {
                            filenameSearch = temp[1];
                            Read(filenameSearch);
                        }
                        if (temp[0] == "ROUTING")
                        {
                            System.Console.WriteLine("SERW:ROUTING");
                            odbiorca = temp[1];
                            filenameSearch = temp[2];
                            for (int i = 3; i < temp.Length; i++)
                            {
                                downers.Add(temp[i]);
                            }
                            streamwriter.WriteLine("ROUTING-OK");
                            streamwriter.Flush();
                            request = streamreader.ReadLine();
                            if (request == "GO")
                            {
                                Read(filenameSearch);
                            }
                        ab:
                            int rand = Rand();
                        for (int i = 0; i < downers.Count(); i++)
                        {
                            if (downers[i] == parent.clients.ElementAt(rand).ip)
                                goto ab;
                        }
                            if (parent.clients.ElementAt(rand).ip == odbiorca)
                                parent.clients.ElementAt(rand).Send(directory + "\\", filenameSearch);
                            else
                                downers.Add(parent.GetMyIP());
                                parent.clients.ElementAt(rand).SendPass(directory + "\\" + filenameSearch, odbiorca, downers);
                            
                            parent.LoadTheDir();
                            File.Delete(parent.sciezka + "/" + filenameSearch); //USUWANIE 
                        }
                   
                    for (int i = 1; i < temp.Length; i++)
                    {
                        askers.Add(temp[i]);
                    }
                    }
                    catch (NullReferenceException)
                    {
                        continue;
                    }
                    System.Console.WriteLine("---------");
                    System.Console.WriteLine(Rand());
                    System.Console.WriteLine("---------");
                    System.Console.WriteLine("REQUEST: " + request);
                    System.Console.WriteLine("ASKER " + asker);
                    System.Console.WriteLine("ILU WIDZE KLIENTOW :" + parent.clients.Count());
                    if (request == "DISCONNECTING")
                    {
                       
                        streamwriter.WriteLine("OK-DISCONNECT");
                        streamwriter.Flush();
                        streamreader.Close();
                        streamwriter.Close();
                        networkStream.Close();
                        socket.Close();
                        break;
                    }
                    if (request == "Search")
                    {
                        route = false;
                        System.Console.WriteLine("SERW: SEARCH-OK");
                        streamwriter.WriteLine("SEARCH-OK");
                        streamwriter.Flush();
                        request = streamreader.ReadLine();
                        filenameSearch = request;
                        System.Console.WriteLine("SERW: szukam " + request);
                        if (CheckAvailibity(filenameSearch, ref directory))
                        {
                            MessageBox.Show("File found, press OK to start downloading");
                            System.Console.WriteLine("SERW: DOWNLOADING");
                            streamwriter.WriteLine("DOWNLOADING");
                            streamwriter.Flush();
                            request = streamreader.ReadLine();

                            if (request == "START")
                            {
                                int rand = Rand();
                                if (parent.clients.ElementAt(rand).ip == odbiorca)
                                    parent.clients.ElementAt(rand).Send(directory + "\\", filenameSearch);
                                else
                                    downers.Add(parent.GetMyIP());
                                    parent.clients.ElementAt(rand).SendPass(directory + "\\" + filenameSearch, odbiorca, downers);
                            }

                        }
                        else
                        {
                            int askerID = new int();
                            for (int i = 0; i < parent.clients.Count(); i++)
                            {
                                if (parent.clients.ElementAt(i).serverName == asker)
                                    askerID = i;
                            }
                            int tm = parent.clients.Count() - askers.Count();
                            if (parent.clients.Count() == 1 || tm == 0)
                            {
                                streamwriter.WriteLine("NOTFOUND");
                                streamwriter.Flush();
                            }
                            else
                            {
                                System.Random x = new Random(System.DateTime.Now.Millisecond);
                            abc:
                                int pom = 0;
                                int a = x.Next(0, parent.clients.Count() - 1);
                                for (int i=0; i<askers.Count(); i++)
                                {
                                    if (askers[i] == parent.clients.ElementAt(a).ip)
                                        pom++;
                                }
                                if (pom > 0)
                                    goto abc;
                                else
                                    askers.Add(parent.GetMyIP());
                                    parent.clients.ElementAt(a).PushSearch(filenameSearch, askers);
                            }
                        }

                    }
                    if (request == "")
                    {
                        streamwriter.WriteLine("UNKNOWN-REQUEST");
                        streamwriter.Flush();
                    }

                } while (true);

            }

            public void Koniec()
            {
                a.Abort();
            }

            public int Rand()
            {
                int askerID = new int();
                int parameter = 70;

                for (int i = 0; i < parent.clients.Count(); i++)
                {
                    if (parent.clients.ElementAt(i).serverName == asker)
                        askerID = i;
                    System.Console.WriteLine("askerID: " + i);
                    System.Console.WriteLine(parent.clients.ElementAt(i).serverName + " : " + asker);
                }

                System.Random x = new Random(System.DateTime.Now.Millisecond);
                int a = x.Next(1, 100);
                int reszta = 100 - parameter;
                if (parent.clients.Count() > 1)
                {
                    System.Console.WriteLine("Losuje w stosunku " + parameter + ":" + reszta);
                    if (a > parameter)
                    {
                        System.Console.WriteLine("wylosowalem odbiorce: parent.clients.ElementAt(" + askerID + ")");
                        return askerID;
                    }
                    else
                    {
                        System.Console.WriteLine("wybieram sposrod reszty...");
                        System.Random xx = new Random(System.DateTime.Now.Millisecond);
                    abc:
                        int index = xx.Next(0, parent.clients.Count());
                        System.Console.WriteLine("wylosowalem " + index);
                        if (index == askerID)
                            goto abc;
                        else
                        {
                            System.Console.WriteLine("wylosowalem: parent.clients.ElementAt(" + index + ")");
                            return index;
                        }
                    }

                }
                else
                {
                    System.Console.WriteLine("wybieram odbiorce (jest tylko jeden klient): parent.clients.ElementAt(" + askerID + ")");
                    return askerID;
                }
            }
            
            public void WaitingForRequest()
            {

                request = streamreader.ReadLine();
                if (request == "Hello aP2P Server")
                {
                    streamwriter.WriteLine("Hello aP2P Client");
                    streamwriter.Flush();
                    request = streamreader.ReadLine();
                    if (request == "Connection established")
                    {


                        a = new Thread(new ThreadStart(funkcja));
                        a.Start();
                        if (parent.suspend)
                            a.Abort();
                    }

                }
            }
        }




        public void Run()
        {
            IPAddress localAddr = IPAddress.Parse(GetMyIP());
            TcpListener tcpListener = new TcpListener(localAddr, 65000);
            tcpListener.Start();



            for (; !koniec; )
            {
                Socket socketForClient = tcpListener.AcceptSocket();

                if (socketForClient.Connected)
                {
                    Console.WriteLine("Klient zostal podlaczony");


                    ClientHandler handler = new ClientHandler(socketForClient, this);

                    handler.WaitingForRequest();

                }
            }
        }
    }
}

